//
//  APIResult.swift
//  
//
//  Created by bo wang on 2020/11/19.
//  Copyright © 2020 wangbo. All rights reserved.
//

import UIKit
import YYModel
import Alamofire

/// 未指定请求结果数据模型时使用的数据模型。此时数据将会保存在dataDict中。
class UndefinedModel: NSObject {
    @objc private(set) var dataDict: [String: Any]?
    @objc private(set) var dataArray: [Any]?

    init?(dict: [String: Any]? = nil, array: [Any]? = nil) {
        if dict == nil && array == nil {
            return nil
        }
        dataDict = dict
        dataArray = array
    }

    subscript(_ key: String) -> Any? {
        dataDict?[key]
    }
    subscript(_ index: Int) -> Any? {
        if let array = dataArray, (0..<array.count) ~= index {
            return array[index]
        }
        return nil
    }
    var count: Int? { dataDict?.count ?? dataArray?.count }
}

typealias NormalResult = APIResult<UndefinedModel>

/// 服务端API请求结果模型。
class APIResult<T: NSObject>: NSObject {
    
    /// 原始响应数据字符串
    private(set) lazy var plainText: String? = {
        guard let data = resp?.data else { return nil }
        return String(data: data, encoding: .utf8)
    }()

    /// 原始json数据
    private(set) var json: [String: Any]?
    /// 原始json数据，如果是数组的话
    private(set) var array: [Any]?
    
    /// 原始响应数据
    private(set) var resp: AFDataResponse<Data>?
    
    /// 响应的状态码
    var statusCode: Int? {
        resp?.response?.statusCode
    }
    
    /// 服务端返回的result值
    var resultCode: Int? {
        safe_int_or_nil(json?["result"])
    }

    /// 原始错误信息。实际类型为 AFError
    private(set) var error: Error?
    
    /// 返回信息的 info / message / 原始内容
    private(set) lazy var info: String? = (json?["info"] as? String) ?? (json?["message"] as? String) ?? plainText
    
    /// 服务端返回的Errors内容中的错误信息，如果返回多个错误信息，取第一个。
    private(set) lazy var serverError: APIServerError? = {
        if !serverErrors.isEmpty {
            return serverErrors.first
        }
        if let err = (json?["errors"] as? [String: Any]) ?? (json?["error"] as? [String: Any]) {
            return APIServerError.yy_model(withJSON: err)
        }
        if let err = (json?["errors"] as? String) ?? (json?["error"] as? String) {
            return APIServerError(with: err)
        }
        return nil
    }()
    /// 服务端返回的Errors内容中的多个错误信息，如果有的话。
    private(set) lazy var serverErrors: [APIServerError] = {
        if let errors = (json?["errors"] as? [Any]) ?? (json?["error"] as? [Any]) {
            return [APIServerError].yy_model(with: errors) ?? []
        }
        return []
    }()

    /// 请求是否成功
    private(set) lazy var suc: Bool = {
        
        if error != nil {
            return false
        }
        
        let serverCode = safe_int_or_nil(json?["code"])
        let resultCode = safe_int_or_nil(json?["result"])
        
        return serverCode == 200 || resultCode == 1
    }()
    
    private var dataKey = "data"

    /// model化的数据
    private(set) lazy var data: T? = {
        let dataDict = json?[dataKey] as? [String: Any]
        let dataArray = json?[dataKey] as? [Any]
        
        if T.self == UndefinedModel.self {
            return UndefinedModel(dict: dataDict, array: dataArray) as? T
        }
        if let j = dataDict {
            return T.yy_model(with: j)
        }
        return nil
    }()
    
    /// model化的数组数据。原始数据非数组时返回nil。T为UndefinedModel时返回nil
    private(set) lazy var dataList: [T]? = {
        if T.self == UndefinedModel.self {
            return nil
        }
        if let a = json?[dataKey] as? [Any] {
            return [T].yy_model(with: a)
        }
        return nil
    }()
    
    convenience init(resp: AFDataResponse<Data>) {
        switch resp.result {
        case .success(let data):
            self.init(resp: resp, data: data, error: nil)
        case .failure(let error):
            self.init(resp: resp, data: resp.data, error: error)
        }
    }
    
    private init(resp: AFDataResponse<Data>, data: Data?, error: AFError?) {
        super.init()
        
        self.resp = resp
        self.error = error
        
        if let data = data, let obj = try? JSONSerialization.jsonObject(with: data) {
            if let jsonData = obj as? [String: Any] {
                self.json = jsonData
            } else if let arrayData = obj as? [Any] {
                self.array = arrayData
            }
        }
    }

    /// 可读的错误信息。
    private(set) lazy var errDesc: String = {
        serverError?.message ?? (error as? AFError)?.errDesc ?? info ?? "Request Failed"
    }()
}

extension AFError {
    var errDesc: String? {
        switch self {
        case .createUploadableFailed(let error):
            return error.localizedDescription
        case .createURLRequestFailed(let error):
            return error.localizedDescription
        case .downloadedFileMoveFailed(let error, _, _):
            return error.localizedDescription
        case .explicitlyCancelled:
            return "Cancelled"
        case .invalidURL:
            return "Invalid URL"
        case .multipartEncodingFailed:
            return "Encoding Error"
        case .parameterEncodingFailed:
            return "Encoding Error"
        case .parameterEncoderFailed:
            return "Encoder Error"
        case .requestAdaptationFailed(let error):
            return error.localizedDescription
        case .requestRetryFailed(let retryError, _):
            return retryError.localizedDescription
        case .responseValidationFailed:
            return "Invalid Response"
        case .responseSerializationFailed:
            return "Invalid Response Data"
        case .serverTrustEvaluationFailed:
            return "Illegal Request"
        case .sessionDeinitialized:
            return "Invalid Request"
        case .sessionInvalidated(let error):
            return error?.localizedDescription ?? "Invalid Request"
        case .sessionTaskFailed(let error):
            return error.localizedDescription
        case .urlRequestValidationFailed:
            return "Invalid Request"
        }
    }
}
